using System;

using System.Runtime.InteropServices;

using PVOID = System.IntPtr;
using DWORD = System.UInt32;


namespace usb_api
{
	unsafe public class usb_interface
	{
		#region  String Definitions of Pipes and VID_PID
		//string vid_pid_boot= "vid_04d8&pid_000b";    // Bootloader vid_pid ID
		string vid_pid_norm= "vid_04d8&pid_000c";

		string out_pipe= "\\MCHP_EP1"; // Define End Points
		string in_pipe= "\\MCHP_EP1";
		#endregion

		#region Imported DLL functions from mpusbapi.dll
		[DllImport("mpusbapi.dll")]
		private static extern DWORD _MPUSBGetDLLVersion();
		[DllImport("mpusbapi.dll")]
		private static extern DWORD _MPUSBGetDeviceCount(string pVID_PID);
		[DllImport("mpusbapi.dll")]
		private static extern void* _MPUSBOpen(DWORD instance,string pVID_PID,string pEP,DWORD dwDir,DWORD dwReserved);
		[DllImport("mpusbapi.dll")]
		private static extern DWORD _MPUSBRead(void* handle,void* pData,DWORD dwLen,DWORD* pLength,DWORD dwMilliseconds);
		[DllImport("mpusbapi.dll")]
		private static extern DWORD _MPUSBWrite(void* handle,void* pData,DWORD dwLen,DWORD* pLength,DWORD dwMilliseconds);
		[DllImport("mpusbapi.dll")]
		private static extern DWORD _MPUSBReadInt(void* handle,DWORD dwLen,DWORD* pLength,DWORD dwMilliseconds);
		[DllImport("mpusbapi.dll")]
		private static extern bool _MPUSBClose(void* handle);
		#endregion

		void* myOutPipe;
		void* myInPipe;

		private void OpenPipes()
		{
			DWORD selection=0; // Selects the device to connect to, in this example it is assumed you will only have one device per vid_pid connected.

			myOutPipe = _MPUSBOpen(selection,vid_pid_norm,out_pipe,0,0);
			myInPipe = _MPUSBOpen(selection,vid_pid_norm,in_pipe,1,0);
		}
		private void ClosePipes()
		{
			_MPUSBClose(myOutPipe);
			_MPUSBClose(myInPipe);
		}

		private DWORD SendReceivePacket(byte* SendData, DWORD SendLength, byte* ReceiveData, DWORD *ReceiveLength)
		{
			uint SendDelay=1000;
			uint ReceiveDelay=1000;

			DWORD SentDataLength;
			DWORD ExpectedReceiveLength = *ReceiveLength;

			OpenPipes();

				if(_MPUSBWrite(myOutPipe,(void*)SendData,SendLength,&SentDataLength,SendDelay)==1)
				{

					if(_MPUSBRead(myInPipe,(void*)ReceiveData, ExpectedReceiveLength,ReceiveLength,ReceiveDelay)==1)
					{
						if(*ReceiveLength == ExpectedReceiveLength)
						{
							ClosePipes();
							return 1;   // Success!
						}
						else if(*ReceiveLength < ExpectedReceiveLength)
						{
							ClosePipes();
							return 2;   // Partially failed, incorrect receive length
						}
					}
				}
			ClosePipes();
			return 0;  // Operation Failed
		}

		public DWORD GetDLLVersion()
		{
			return _MPUSBGetDLLVersion();
		}

        public DWORD GetDeviceCount()
        {
            return _MPUSBGetDeviceCount(vid_pid_norm);
        }
        //public DWORD GetDeviceCount(string Vid_Pid)
        //{

        //    return _MPUSBGetDeviceCount(Vid_Pid);
        //}

		public int UpdateLED(uint led, bool State)
		{
			// The default demo firmware application has a defined application
			// level protocol.
			// To set the LED's, the host must send the UPDATE_LED
			// command which is defined as 0x32, followed by the LED to update,
			// then the state to chance the LED to.
			//
			// i.e. <UPDATE_LED><0x01><0x01>
			//
			// Would activate LED 1
			//
			// The receive buffer size must be equal to or larger than the maximum
			// endpoint size it is communicating with. In this case, it is set to 64 bytes.

			byte* send_buf=stackalloc byte[64];
			byte* receive_buf=stackalloc byte[64];

			DWORD RecvLength=3;
			send_buf[0] = 0x32;			//Command for LED Status  
			send_buf[1] = (byte)led;
			send_buf[2] = (byte)(State?1:0);
            //SendReceivePacket(byte* SendData, DWORD SendLength, byte* ReceiveData, DWORD *ReceiveLength)
			if (SendReceivePacket(send_buf,3,receive_buf,&RecvLength) == 1)
			{
				if (RecvLength == 1 && receive_buf[0] == 0x32)
				{	
					return 0;
				}
				else
				{
					return 2;
				}
			}
			else
			{	
				return 1;
			}
		}
        public uint Sum(uint sohang1, uint sohang2, uint sohang3, uint sohang4, uint sohang5)
        {
            uint tong = 0;
            tong = sohang1 + sohang2 + sohang3 + sohang4 + sohang5;
            return tong;
        }
        public void Add(ref int a, ref int b, ref int c, ref int d, ref int e, ref int f)
		{
			// The default demo firmware application has a defined application
			// level protocol.
			// To calculate the sum the host must send the DO_SUM
			// command which is defined as 0xEE, followed by the LED to update,
			// then the state to chance the LED to.
			//
			// i.e. <DO_SUM><0x01><0x02>
			//
			// Would return a command
			//
			// <DO_SUM><0x03>
			//
			// Bear in mind no error correction to prevent roll over in the PIC has 
			// been implemented, to keep this example as simple as possible.
			//
			// The receive buffer size must be equal to or larger than the maximum
			// endpoint size it is communicating with. In this case, it is set to 64 bytes.

			byte* send_buf=stackalloc byte[64];
			byte* receive_buf=stackalloc byte[64];


			DWORD RecvLength=(byte)21;
			
	
			if (SendReceivePacket(send_buf,0,receive_buf,&RecvLength) == 1)
			{
                int byte0, byte1, byte2, byte3;
                int byte4, byte5, byte6, byte7;
                int byte8, byte9, byte10, byte11;
                int byte12, byte13, byte14, byte15;
                int byte16, byte17, byte18, byte19;

                byte0 = receive_buf[0];
                byte1 = receive_buf[1]<<8;
                byte2 = receive_buf[2]<<16;
                byte3 = receive_buf[3]<<24;
                a = byte0 + byte1 + byte2 + byte3;

                byte4 = receive_buf[4];
                byte5 = receive_buf[5] << 8;
                byte6 = receive_buf[6] << 16;
                byte7 = receive_buf[7] << 24;
                b = byte4 + byte5 + byte6 + byte7;

                byte8 = receive_buf[8];
                byte9 = receive_buf[9] << 8;
                byte10 = receive_buf[10] << 16;
                byte11 = receive_buf[11] << 24;
                c = byte8 + byte9 + byte10 + byte11;

                byte12 = receive_buf[12];
                byte13 = receive_buf[13] << 8;
                byte14 = receive_buf[14] << 16;
                byte15 = receive_buf[15] << 24;
                d = byte12 + byte13 + byte14 + byte15;

                byte16 = receive_buf[16];
                byte17 = receive_buf[17] << 8;
                byte18 = receive_buf[18] << 16;
                byte19 = receive_buf[19] << 24;
                e = byte16 + byte17 + byte18 + byte19;
    
                f = receive_buf[20];
			}
		}
	}

}
